/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.community.component.spreadsheet.prepare;

import cn.ac.nercis.pes.common.constant.evaluation.SecurityThreatMode;
import cn.ac.nercis.pes.common.constant.knowledge.ThreatType;
import cn.ac.nercis.pes.community.component.spreadsheet.TableSheetView;
import cn.ac.nercis.pes.community.model.vo.evaluation.SecurityThreatVO;
import cn.ac.nercis.pes.community.support.event.EventCallback;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.controlsfx.control.spreadsheet.GridBase;
import org.controlsfx.control.spreadsheet.SpreadsheetCell;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 安全威胁表控件
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
public class SecurityThreatSheetView extends TableSheetView<SecurityThreatVO> {
    private final static Integer EMPTY_DATA_COUNT = 3;
    private final static List<String> COLUMNS = List.of("序号","安全层面","威胁分类");
    private final static Integer CODE_COLUMN_INDEX = 0;
    private final static Integer TYPE_COLUMN_INDEX = 1;
    private final static Integer NAME_COLUMN_INDEX = 2;

    private final ObservableList<String> typeList;

    private EventCallback<Set<String>> onDeleteAction;
    private String systemId;

    public SecurityThreatSheetView(){
        this.getStyleClass().add("pes-sheet-view");
        typeList = FXCollections.observableArrayList(ThreatType.findThreatTypeNames());

        var grid = new GridBase(EMPTY_DATA_COUNT, COLUMNS.size());
        grid.getColumnHeaders().addAll(COLUMNS);
        setGrid(grid);
        getColumns().get(CODE_COLUMN_INDEX).setPrefWidth(100);
        getColumns().get(TYPE_COLUMN_INDEX).setPrefWidth(200);
        getColumns().get(NAME_COLUMN_INDEX).setPrefWidth(600);
//        this.setRequiredColumns();
    }

    public void initialize(String systemId,EventCallback<Set<String>> deleteAction){
        this.systemId = systemId;
        this.onDeleteAction = deleteAction;
        this.editModel.set(false);
    }

    @Override
    public void loadContent(List<SecurityThreatVO> data) {
        this.editModel.set(false);
        this.sheetData = data;
        this.clearContent();
        for (var dataIndex = 0; dataIndex < data.size(); dataIndex++) {
            this.loadContent(data.get(dataIndex), toSheetRowIndex(dataIndex));
        }
        appendEmptyDataRow();
        setGrid(getGrid());
        this.editModel.set(true);
    }

    public List<SecurityThreatVO> getModifyData() {
        List<SecurityThreatVO> data = new ArrayList<>();
        for (var sheetRowIndex = 0; sheetRowIndex < this.getGrid().getRows().size(); sheetRowIndex++) {
            if (this.isEmptyData(sheetRowIndex)) {
                continue;
            }
            data.add(getModifyData(sheetRowIndex));
        }
        return data.stream()
                .filter(item -> Objects.nonNull(item) && item.isChange())
                .collect(Collectors.toList());
    }

    protected void appendEmptyDataRow() {
        AtomicInteger sheetRowIndex = new AtomicInteger(this.getGrid().getRows().size());
        for (var index = 0; index < DEFAULT_APPEND_DATA_COUNT; index++) {
            this.getGrid().getRows().add(generateEmptyDataRow(sheetRowIndex.getAndIncrement()));
        }
    }

    private SecurityThreatVO getModifyData(Integer sheetRowIndex) {
        if (toDataIndex(sheetRowIndex) < 0) {
            return null;
        }
        var rowData = this.getGrid().getRows().get(sheetRowIndex);
        SecurityThreatVO info;
        if (toDataIndex(sheetRowIndex) < this.sheetData.size()) {
            info = this.sheetData.get(toDataIndex(sheetRowIndex));
        } else {
            info = new SecurityThreatVO();
            info.setSystemId(this.systemId);
            info.setMode(SecurityThreatMode.CUSTOM);
        }
        info.setCode(rowData.get(CODE_COLUMN_INDEX).getText());
        info.setType(rowData.get(TYPE_COLUMN_INDEX).getText());
        info.setName(rowData.get(NAME_COLUMN_INDEX).getText());
        return info;
    }

    @Override
    protected void loadContent(SecurityThreatVO info, Integer sheetRowIndex) {
        final ObservableList<SpreadsheetCell> data = FXCollections.observableArrayList();
        AtomicInteger columnIndex = new AtomicInteger(0);
        if (Objects.equals(info.getMode(), SecurityThreatMode.KNOWLEDGE)){
            data.add(getTextFieldCell(sheetRowIndex,columnIndex.getAndIncrement(),info.getCode(),false));
            data.add(getTextFieldCell(sheetRowIndex,columnIndex.getAndIncrement(),info.getType(),false));
            data.add(getTextFieldCell(sheetRowIndex,columnIndex.getAndIncrement(),info.getName(),false));
        } else {
            data.add(getTextFieldCell(sheetRowIndex,columnIndex.getAndIncrement(),info.getCode(),true));
            data.add(getComboBoxCell(sheetRowIndex,columnIndex.getAndIncrement(),typeList,info.getType()));
            data.add(getTextFieldCell(sheetRowIndex,columnIndex.getAndIncrement(),info.getName(),true));
        }
        this.getGrid().getRows().add(data);
        info.resetChange();
    }

    @Override
    protected void clearContent() {
        int endIndex = this.getGrid().getRows().size() - 1;
        for (; endIndex >= 0; endIndex--) {
            this.getGrid().getRows().remove(endIndex);
        }
    }

    @Override
    protected boolean isEmptyData(Integer sheetRowIndex) {
        if(toDataIndex(sheetRowIndex) < 0){
            return true;
        } else if(toDataIndex(sheetRowIndex) < this.sheetData.size()){
            return false;
        }
        var rowData = this.getGrid().getRows().get(sheetRowIndex);
        return StringUtils.isBlank(rowData.get(NAME_COLUMN_INDEX).getText())
                && StringUtils.isBlank(rowData.get(CODE_COLUMN_INDEX).getText())
                && StringUtils.isBlank(rowData.get(TYPE_COLUMN_INDEX).getText());
    }

    @Override
    protected boolean validate(ObservableList<SpreadsheetCell> rowData) {
        boolean validResult = true;
        if (StringUtils.isBlank(rowData.get(CODE_COLUMN_INDEX).getText())) {
            rowData.get(CODE_COLUMN_INDEX).getStyleClass().add(REQUIRED_CELL_STYLE);
            validResult = false;
        } else {
            rowData.get(CODE_COLUMN_INDEX).getStyleClass().remove(REQUIRED_CELL_STYLE);
        }
        if (StringUtils.isBlank(rowData.get(TYPE_COLUMN_INDEX).getText())) {
            rowData.get(TYPE_COLUMN_INDEX).getStyleClass().add(REQUIRED_CELL_STYLE);
            validResult = false;
        } else {
            rowData.get(TYPE_COLUMN_INDEX).getStyleClass().remove(REQUIRED_CELL_STYLE);
        }
        if (StringUtils.isBlank(rowData.get(NAME_COLUMN_INDEX).getText())) {
            rowData.get(NAME_COLUMN_INDEX).getStyleClass().add(REQUIRED_CELL_STYLE);
            validResult = false;
        } else {
            rowData.get(NAME_COLUMN_INDEX).getStyleClass().remove(REQUIRED_CELL_STYLE);
        }
        return validResult;
    }

    @Override
    protected void clearValidate(ObservableList<SpreadsheetCell> rowData) {
        rowData.get(CODE_COLUMN_INDEX).getStyleClass().remove(REQUIRED_CELL_STYLE);
        rowData.get(TYPE_COLUMN_INDEX).getStyleClass().remove(REQUIRED_CELL_STYLE);
        rowData.get(NAME_COLUMN_INDEX).getStyleClass().remove(REQUIRED_CELL_STYLE);
    }

    @Override
    protected List<String> getDataColumns() {
        return COLUMNS;
    }

    @Override
    protected void deleteData() {
        if (Objects.nonNull(this.onDeleteAction)) {
            var selectIds = this.selectedSheetRowIndex()
                    .stream()
                    .filter(sheetRowIndex -> toDataIndex(sheetRowIndex) >= 0 && toDataIndex(sheetRowIndex) < this.sheetData.size())
                    .map(sheetRowIndex -> this.sheetData.get(toDataIndex(sheetRowIndex)))
                    .filter(st -> Objects.equals(st.getMode(), SecurityThreatMode.CUSTOM))
                    .map(SecurityThreatVO::getId)
                    .collect(Collectors.toSet());
            this.onDeleteAction.action(selectIds);
        }
    }

    @Override
    protected ObservableList<SpreadsheetCell> generateEmptyDataRow(int dataIndex) {
        final ObservableList<SpreadsheetCell> data = FXCollections.observableArrayList();
        AtomicInteger columnIndex = new AtomicInteger(0);
        data.add(getTextFieldCell(toSheetRowIndex(dataIndex),columnIndex.getAndIncrement()));
        data.add(getComboBoxCell(toSheetRowIndex(dataIndex),columnIndex.getAndIncrement(),typeList));
        data.add(getTextFieldCell(toSheetRowIndex(dataIndex),columnIndex.getAndIncrement()));
        return data;
    }

}